#pragma once

#include "qconf.h"
#include "timer.h"

namespace qio {

class Timer;

class EventLoopCallbacks;

class EventLoop {
  friend class EventLoopCallbacks;

  using RunCallback = std::function<void()>;
  using TerminateCallback = std::function<void()>;

 public:
  typedef enum { INIT, RUNNING, POST_STOP, STOPPING, STOPPED } LoopState;

 public:
  virtual ~EventLoop() QIO_NOEXCEPT;

  EventLoop() QIO_NOEXCEPT;

  void Run() QIO_NOEXCEPT;

  void Stop() QIO_NOEXCEPT;

  void RunInLoop(RunCallback &&request) QIO_NOEXCEPT;

  SharedTimer RunEvery(std::size_t ms, Timer::TimerCallback &&cb) QIO_NOEXCEPT;

  SharedTimer RunAfter(std::size_t ms, Timer::TimerCallback &&cb) QIO_NOEXCEPT;

  inline void OnTerminate(TerminateCallback &&cb) QIO_NOEXCEPT {
    loop_stop_cb_ = std::forward<TerminateCallback>(cb);
  }

  inline bool IsInLoop() const QIO_NOEXCEPT {
    return std::this_thread::get_id() == loop_thread_id_;
  }

  inline LoopState GetLoopState() const QIO_NOEXCEPT {
    return loop_state_.load(std::memory_order_acquire);
  }

  inline uv_loop_t *GetRawLoop() const QIO_NOEXCEPT { return loop_; }

  DISABLE_CPY_MOV(EventLoop)

 private:
  virtual void ShouldCleanupLoop() QIO_NOEXCEPT;

 private:
  std::recursive_mutex lock_;
  std::list<RunCallback> request_queue_;  // NOLINT
  uv_loop_t *loop_;
  uv_async_t *async_;
  std::atomic<LoopState> loop_state_;
  std::thread::id loop_thread_id_;
  std::unordered_set<SharedTimer> timer_set_;
  TerminateCallback loop_stop_cb_;
};

}  // namespace qio
